home *** CD-ROM | disk | FTP | other *** search
/ NetNews Offline 2 / NetNews Offline Volume 2.iso / news / comp / lang / c-part1 / 4822 < prev    next >
Encoding:
Text File  |  1996-08-05  |  13.4 KB  |  532 lines

  1. Path: news.itl.net!newsmaster@olympus.itl.net
  2. From: pauls@dyna.mhs.compuserve.com (Paul Seymour)
  3. Newsgroups: comp.lang.c
  4. Subject: Recovery from Division By Zero
  5. Date: Wed, 07 Feb 1996 05:46:03 GMT
  6. Organization: Supernet
  7. Message-ID: <4f8hvg$lbj@fhbgb1.itl.net>
  8. Reply-To: pauls@dyna.mhs.compuserve.com
  9. NNTP-Posting-Host: dialup058.itl.net
  10. Keywords: Signal, Divizion By Zero, ANSI, MSDOS, WATCOM
  11. X-Newsreader: Forte Free Agent 1.0.82
  12.  
  13.     Reply To: pauls@dyna.mhs.compuserve.com
  14.  
  15.     I am porting a MSDOS application from 16Bit MS Visual C++ 1.5 to,
  16. a 32Bit DOS4GW Watcom C++ v10.0a application. The application has
  17. grown too large for conventional memory. The port has been successful
  18. including,an interrupt handler for (IRQ5, INT0xD) which resets the
  19. program by, longjmp() 'ing back to where the program was first
  20. initialised.
  21.     The problem is implementing a handler for division by 0, and other
  22. maths exceptions, that are not handled by matherr. The program needs
  23. to handle the DIV0 exception, and reset back to a know good state. The
  24. way this was successfully done in the 16bit MSVC version was to setup
  25. an interrupt handler for INT0. Which then puts an error message on the
  26. display and longjmp() 's back to the same point as the INT0xD handler
  27. does.
  28.     This method does no work with Watcom C, so I thought I'd defined a
  29. SIG_FPE handler instead, using the ANSI C signal function. This does
  30. not work either, under Watcom C. So I wrote two complete test programs
  31. to test with different,compilers. I still have to use Watcom C to
  32. build my program (3rd party libraries etc.), but I wanted to confirm
  33. that my test programs would work.
  34.     The program that uses the interrupt method has direct text output
  35. routines so that interrupt handlers can display output. Using
  36. _dos_setvect() to register an interrupt vector worked for the INT0xD
  37. vector under DOS4Gw, but it does not seem to be working for the INT0
  38. vector. Also I would like to know why signal() does not work on all
  39. the compilers as it is part of the ANSI C library. Is there a general
  40. fault in the implementation of signal for SIG_FPE on most compilers
  41. foe MS_DOS?
  42.  
  43.  
  44.                              |   Signal                   |  Interrupt
  45. Borland C++ v3.1  | Works                      | Works
  46. MS-Vis C++ v1.5  | crashes with rutime error  | Works in Large Model
  47. WatcomC  v1.0a  | See Below                  | See Below
  48. DJGPP 1.1.1m5    | crashes                    | needs porting
  49.  
  50. Notes on Watcom C:
  51. With -d2 and -d3 debugging both programs crash with
  52.     a division by 0 runtime error.
  53. With -d0 and -d1 the program does not crash by the
  54.     DIV0 handler does not get called and the program is not
  55.     reset.
  56.  
  57.  
  58. /* Test Program 1 */
  59. /**********************************************************/
  60. /***                                                    ***/
  61. /*** TITLE   : cc.c                               ***/
  62. /***                                                    ***/
  63. /*** PURPOSE : To test the divide by zero handler. ***/
  64. /***           SIGNAL based***/
  65. /***                                                    ***/
  66. /**********************************************************/
  67.  
  68. #include <stdio.h>
  69. #include <stdlib.h>
  70. #include <signal.h>
  71. #include <setjmp.h>
  72. #include <conio.h>
  73. #include <float.h>
  74.  
  75. void RestartProgram();
  76. void sigDivideByZero(int sig);
  77. #ifdef __GNUC__
  78.     itoa(int number, char *buffer, int radix);
  79.     #define _fpreset()
  80. #endif
  81.  
  82. static sig_atomic_t DIVZ_sig_count;
  83. static char count_buffer[80];
  84. jmp_buf env;
  85.  
  86. int main()
  87. {
  88.     int f;
  89.     int key;
  90.  
  91.     int dbg_x, dbg_i = 1, dbg_j = 0, dbg_k = 0;
  92.     void (*sigDivideByZero_Orig)(int);
  93.  
  94.  
  95.     sigDivideByZero_Orig = signal(SIGFPE, sigDivideByZero);
  96.     if(sigDivideByZero_Orig == SIG_ERR) {
  97.     puts("");
  98.     puts("Could not establish a new DIV0 handler");
  99.     puts("");
  100.     return 1;
  101.     }
  102.  
  103.  
  104.     if (setjmp(env) == -1) {
  105.     itoa(DIVZ_sig_count, count_buffer, 10);
  106.     puts(count_buffer);
  107.     puts("The DIV by ZERO interupt has been called the above times");
  108.     if(signal(SIGFPE, sigDivideByZero) == SIG_ERR) {
  109.         puts("");
  110.         puts("Could not establish a new DIV0 handler");
  111.         puts("");
  112.         return 1;
  113.     }
  114.  
  115.     }
  116.  
  117.     while(1)
  118.     {
  119.     if(kbhit()) {
  120.         key = getch();
  121.         switch (key) {
  122.         case '0':
  123.         puts("There should be a divide by zero here");
  124.         dbg_x = dbg_i / dbg_j;
  125.         dbg_x = dbg_j / dbg_k;
  126.         break;
  127.         case 'x':
  128.         goto exit;
  129.         case 'X':
  130.         goto exit;
  131.         case 'q':
  132.         goto exit;
  133.         case 'Q':
  134.         goto exit;
  135.         }
  136.     }
  137.     }
  138.  
  139. exit:
  140. /*    signal(SIGFPE, SIG_DFL); */
  141.     if (signal(SIGFPE, sigDivideByZero_Orig) == SIG_ERR) {
  142.     puts("");
  143.     puts("Could not re-establish the original DIV0 handler");
  144.     puts("");
  145.     return 2;
  146.     }
  147. }
  148.  
  149.  
  150. /**********************************************************
  151. NAME     : sigDivideByZero()
  152. RETURNS  :
  153. PURPOSE  : Trap any divide by zero errors which may occur
  154. COMMENTS :
  155. **********************************************************/
  156.  
  157. void sigDivideByZero(int sig)
  158. {
  159.     DIVZ_sig_count++; /*counts how many times this handler is called*/
  160.  
  161.     puts("");
  162.     puts("Hello from the divide by zero signal handler");
  163.     puts("");
  164.  
  165.     _fpreset();
  166.     RestartProgram();
  167. }
  168.  
  169. /**********************************************************
  170. NAME     : RestartProgram()
  171. RETURNS  :
  172. PURPOSE  : Jump to the program restart point
  173. COMMENTS : Used after motor errors, etc.
  174. **********************************************************/
  175.  
  176. void RestartProgram()
  177. {
  178.     longjmp(env, -1);
  179. }
  180.  
  181. #ifdef __GNUC__
  182. itoa(int number, char *buffer, int radix)
  183. {
  184.     switch(radix) {
  185.     case 10:
  186.     sprintf(buffer, "%d", number);
  187.     default :
  188.     printf("\n\nERROR IN ITOA RADIX EXITING\n\n");
  189.     }
  190. }
  191. #endif
  192.  
  193. /* Test program 1 ends */
  194.  
  195.  
  196. ///////////////////////////////////////////////////////////////////////////
  197.  
  198. /* Test Program 2 */
  199. /**********************************************************/
  200. /***                                                    ***/
  201. /*** TITLE   : dd.c                               ***/
  202. /***                                                    ***/
  203. /*** PURPOSE : To test the divide by zero handler. ***/
  204. /***           INT0 based***/
  205. /***                                                    ***/
  206. /**********************************************************/
  207.  
  208. #define DEBUG_PJ
  209. #define NORM_SETJMP
  210.  
  211. #include <stdio.h>
  212. #include <stdlib.h>
  213. #include <signal.h>
  214. #include <dos.h>
  215. #include <process.h>
  216. #include <string.h>
  217. #include <conio.h>
  218. #include <setjmp.h>
  219. #include "dbg_puts.h"
  220. #ifdef __GNUC__
  221.     #include <go32.h>
  222.     #include <dpmi.h>
  223.     #define _disable() disable()
  224.     #define _enable() enable()
  225. #endif
  226.  
  227. static int dbg_curr_row = 1; /*Start of the screen*/
  228. static char count_buffer[80];
  229. static int DIVZ_int_count;
  230. static unsigned char ports;
  231. void (__interrupt __far *ori_DIVZ_Handler)();
  232.  
  233.  
  234.  
  235. /**********************************************************
  236. NAME     : main(int, char *)
  237. RETURNS  :
  238. PURPOSE  :
  239. COMMENTS :
  240. **********************************************************/
  241. void main()
  242. {
  243.     int f;
  244.     int key;
  245.  
  246.     int dbg_x, dbg_i = 1, dbg_j = 0, dbg_k = 0;
  247.  
  248.     dbg_cls();
  249.     dbg_puts("This is a test program for the divide by zero");
  250.     dbg_puts("handling");
  251.  
  252.     ori_DIVZ_Handler = _dos_getvect(0x0);
  253.  
  254.     _dos_setvect(0, DivideByZero);
  255.  
  256.     if (setjmp(env) == -1) {
  257.     dbg_cls();
  258.     dbg_puts(BLANK_LINE);
  259.     dbg_puts("We have returned from an user handled interupt");
  260.     itoa(DIVZ_int_count, count_buffer, 10);
  261.     dbg_puts(count_buffer);
  262.     dbg_puts("The DIV by ZERO interupt has been called the above times");
  263.     dbg_puts(BLANK_LINE);
  264.     }
  265.  
  266.  
  267.     while(1)
  268.     {
  269.     if(kbhit()) {
  270.         key = getch();
  271.         switch (key) {
  272.         case '0':
  273.         puts("There should be a divide by zero here");
  274.         dbg_x = dbg_i / dbg_j;
  275.         dbg_x = dbg_j / dbg_k;
  276.         break;
  277.         case 'x':
  278.         goto exit;
  279.         case 'X':
  280.         goto exit;
  281.         case 'q':
  282.         goto exit;
  283.         case 'Q':
  284.         goto exit;
  285.         }
  286.     }
  287.     }
  288.  
  289. exit:
  290. /*Restore Original interrupt vectors*/
  291.     _dos_setvect(0x0, ori_DIVZ_Handler);
  292. }
  293.  
  294. /**********************************************************
  295. NAME     : DivideByZero()
  296. RETURNS  :
  297. PURPOSE  : Trap any divide by zero errors which may occur
  298. COMMENTS :
  299. **********************************************************/
  300.  
  301. void __interrupt __far DivideByZero()
  302. {
  303. /*    SystemError(s_divide_zero, -1, TRUE);*/
  304.  
  305.     _disable(); /* a critical section */
  306.  
  307.     DIVZ_int_count++; /*counts how many times this handler is called*/
  308.  
  309. /*
  310. To directly signal the 8259 programmable interrupt controller
  311. that this interrupt is finished.
  312.  
  313. Was not necessary in MSVC, and does not seem to help under
  314. DOS4GW as the interrupt does not seem to get called.
  315. */
  316. #if 0
  317.     ports = (unsigned char) inp(0x0021); /* get IRQ mask status */
  318.     ports |= 0x01;         /* unmask for IRQ 0 */
  319.     outp(0x0021, ports);   /* write back into OCW */
  320.  
  321.     outp(0x0020, 0x20);    /* Signal a non-specific EOI */
  322. #endif
  323.  
  324.  
  325.     dbg_puts(BLANK_LINE);
  326.     dbg_puts("Hello from the divide by zero interupt handler");
  327.     dbg_puts(BLANK_LINE);
  328.  
  329. /*    ori_DIVZ_Handler();*/
  330.  
  331.     _enable();
  332.  
  333.     RestartProgram();
  334. }
  335.  
  336. /**********************************************************
  337. NAME     : RestartProgram()
  338. RETURNS  :
  339. PURPOSE  : Jump to the program restart point
  340. COMMENTS : Used after motor errors, etc.
  341. **********************************************************/
  342.  
  343. void RestartProgram()
  344. {
  345.     longjmp(env, -1);
  346. }
  347.  
  348.  
  349. /***********************************************************************/
  350. #ifdef DEBUG_PJ
  351. /* These functions directly output dbg info to the colour text video
  352. memory.*/
  353. /* It assumes that we are working in 80x25 mode*/
  354. /* It allows text output even in interrupt functions */
  355. /***********************************************************************/
  356.  
  357. /**********************************************************
  358. NAME     : dbg_puts(char *s_to_print)
  359. RETURNS  :
  360. PURPOSE  : puts a string directly to video memory
  361. COMMENTS :
  362. **********************************************************/
  363. int dbg_puts(char *s_to_print)
  364. {
  365.     dbg_curr_row++;
  366.     if (dbg_curr_row > 25)
  367.     dbg_curr_row = 1;
  368.     return dbg_puts_pos(s_to_print, dbg_curr_row, DBG_START_COL);
  369. }
  370.  
  371. /**********************************************************
  372. NAME     : dbg_puts_ID(int ID)
  373. RETURNS  :
  374. PURPOSE  : puts a string directly to video memory
  375. COMMENTS :
  376. **********************************************************/
  377. void dbg_puts_ID(int ID)
  378. {
  379.     char *s_error;
  380.     if (dbg_curr_row > 25)
  381.     dbg_curr_row = 1;
  382.  
  383.     dbg_puts(BLANK_LINE);
  384.  
  385.     switch(ID) {
  386.     case s_divide_zero: s_error = "Divide by zero error!!"; break;
  387.     default:            s_error = "Unknown error error!!"; break;
  388.     }
  389.  
  390.     dbg_puts_pos(s_error, dbg_curr_row, DBG_START_COL);
  391.  
  392.     dbg_puts(BLANK_LINE);
  393. }
  394.  
  395.  
  396. /**********************************************************
  397. NAME     : dbg_puts_pos(char *s_to_print, int row, int col)
  398. RETURNS  :
  399. PURPOSE  : puts a string directly to video memory
  400. COMMENTS : at a specified location
  401. **********************************************************/
  402. int dbg_puts_pos(char *s_to_print, int row, int col)
  403. {
  404.     int num_of_chars_printed = 0;
  405.  
  406. #if __WATCOMC__
  407.     char *VidMem = (char *) 0xB8000;
  408.     /*The colour text absolute memory address*/
  409. #else
  410.     char far *VidMem = (char *) MK_FP(0xB800, 0x0);
  411.     /*The colour text absolute memory address*/
  412. #endif
  413. /*To set the video memory to the start of the row and col we want to
  414. use*/
  415.     row--;
  416.     col--;                      /*Make row and col zero based*/
  417.     VidMem += (row * 80 * 2);
  418.     VidMem += (col * 2);
  419.     while (*s_to_print) {       /*zero terminates*/
  420.     *(VidMem++) = *(s_to_print++);
  421.     VidMem++;       /*twice because vid mem is char, attribute pairs*/
  422.     num_of_chars_printed++;
  423.     }
  424.  
  425.     return num_of_chars_printed;
  426. }
  427.  
  428. /**********************************************************
  429. NAME     : dbg_cls(void)
  430. RETURNS  : void
  431. PURPOSE  : clears the video memory directly
  432. COMMENTS :
  433. **********************************************************/
  434. void dbg_cls(void)
  435. {
  436. #if __WATCOMC__
  437.     char *VidMem = (char *) 0xB8000;
  438.     /*The colour text absolute memory address*/
  439. #else
  440.     char far *VidMem = (char *) MK_FP(0xB800, 0x0);
  441.     /*The colour text absolute memory address*/
  442. #endif
  443.     int posVidMem;
  444.  
  445.     dbg_curr_row = 1;
  446.     for (posVidMem = 0; posVidMem < (80 * 25); posVidMem++) {
  447.     *(VidMem++) = 0;
  448.     VidMem++; /* jump over the attributes */
  449.     }
  450. }
  451.  
  452. /**********************************************************
  453. NAME     : dbg_beep(void)
  454. RETURNS  : void
  455. PURPOSE  : makes a sonic beep
  456. COMMENTS :
  457. **********************************************************/
  458. void dbg_beep(void)
  459. {
  460.     puts("\a\a\a");
  461. }
  462.  
  463. #endif /*DEBUG_PJ*/
  464.  
  465.  
  466. #ifdef __GNUC__
  467. itoa(int number, char *buffer, int radix)
  468. {
  469.     switch(radix) {
  470.     case 10:
  471.     sprintf(buffer, "%d", number);
  472.     default :
  473.     printf("\n\nERROR IN ITOA RADIX EXITING\n\n");
  474.     }
  475. }
  476. #endif
  477.  
  478. /* end of program 2 */
  479.  
  480. //////////////////////////////////////////////////////////////////////////
  481.  
  482. /* the dbg_puts.h header */
  483.  
  484. /* name dbg_puts.h */
  485. #ifdef __GNUC__
  486.     #define __far
  487.     #define _far
  488.     #define far
  489.     #define __interrupt
  490. #endif
  491.  
  492. #ifndef FALSE
  493. #define FALSE 0
  494. #endif
  495. #ifndef TRUE
  496. #define TRUE 1
  497. #endif
  498.  
  499. #if defined(__WATCOMC__) && !defined(NORM_SETJMP)
  500.     pj_jmp_buf env;
  501. #else
  502.     jmp_buf env;
  503. #endif
  504.  
  505.  
  506. #ifdef DEBUG_PJ
  507.     #define DBG_START_COL 1
  508.     #define BLANK_LINE "
  509. "
  510.  
  511.     void dbg_puts_ID(int ID);
  512.     int dbg_puts_pos(char *s_to_print, int row, int col);
  513.     int dbg_puts(char *s_to_print);
  514.     void dbg_cls(void);
  515.     void dbg_beep(void);
  516. #endif
  517.  
  518. void __interrupt __far DivideByZero();
  519. void __interrupt __far COM1_Handler();
  520. int SystemError(int wID, int n, int Error);
  521. void RestartProgram();
  522. void main();
  523. void sigDivideByZero(int sig);
  524.  
  525.  
  526. #define s_divide_zero 10
  527. /* the dbg_puts.h header ends*/
  528.  
  529.  
  530. ////////////////////////////////////////////////////////////////////////////
  531.  
  532.